package com.itany.mall.factory;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import com.itany.mall.dao.UserDao;

// 对象工厂
// 工厂启动时, 读取配置文件
// (从类加载目录加载"objects.properties"的文件)
// 当前项目结中, 与类加载目录(bin)相对应的目录是"src"目录
// 所以该配置文件应当存放于src目录
// 当程序运行时, 会被编译到类加载目录下去
// 获取要创建哪些类的对象的配置信息
// 将目标类的Class对象存入容器
// 工厂提供公共方法, 来获取容器中的对象
// 当进行对象的获取时, 根据情况将目标对象创建出来
public class ObjectFactory {

	// 按照配置配置清单将要创建对象的目标类获取并存入该容器
	private static final Map<String, Class> CLASSES;

	// 根据配置清单将目标类的实例创建出来, 存入该容器
	private static final Map<String, Object> OBJECTS;

	// 静态代码块: 工厂初始化代码
	static {
		// 容器的初始化
		OBJECTS = new HashMap<>();
		CLASSES = new HashMap<>();

		try {
			// 加载配置文件
			Properties properties = new Properties();
			InputStream is = ObjectFactory.class.getClassLoader()
					.getResourceAsStream("objects.properties");
			properties.load(is);

			// 对配置清单遍历
			for (Map.Entry entry : properties.entrySet()) {

				// 从配置中获取出key和要创建实例的目标类(className)
				String key = (String) entry.getKey();
				String className = (String) entry.getValue();

				System.out.println("需要由工厂创建实例: " + key + " ---> " + className);

				// 将目标类的Class对象存入容器备用
				Class c = Class.forName(className);
				CLASSES.put(key, c);
			}

		} catch (Exception e) {
			e.printStackTrace();
			// 如果发生异常, 说明工厂初始化失败
			// 程序中使用工厂就会有问题, 从而导致整个程序都有问题
			// 程序应当终止, 让程序员检查并排除问题
			// 静态代码块中, 能被throw关键字操作的对象只有一个:
			throw new ExceptionInInitializerError("工厂初始化失败");
		}

	}

	// 公共的获取对象的方法
	// 当需要从工厂中获取对象时, 按照示例操作获取
	// 示例: private UserDao userDao = (UserDao) ObjectFactory.getObject("userDao");
	// 从工厂获取对象时, 所使用的key, 在配置清单中必须存在
	public static Object getObject(String key) {
		// 先尝试从对象容器中获取目标对象
		Object obj = OBJECTS.get(key);
		try {
			// 判断是否获取到对象, 如果为null, 说明对象不存在
			if (obj == null) {
				// 从类容器中获取出Class对象, 将目标类的实例创建出来
				// 并存入容器备用
				Class c = CLASSES.get(key);
				obj = c.newInstance();
				OBJECTS.put(key, obj);
			}
		} catch (Exception e) {
			e.printStackTrace();
			// 当发生异常, 则说明从工厂的容器中获取对象失败
			// 从而导致程序运行存在安全隐患
			// 应当让程序终止, 让程序员检查并排除问题
			throw new RuntimeException("从工厂获取对象 " + key + " 失败", e);
		}
		return obj;
	}

}
